﻿// Inner Fire 游戏引擎库
// Scene - 场景类
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 所属层级：应用层
// 用于控制一个场景内的东西。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-01-13

#include "scene.h"
#include <game/core/timer.h>
#include <game/manager/game_manager.h>
#include <game/physics/physics_world.h>
#include <klog.h>
#include <ktime.h>
#include <stop_watch.h>

namespace ifire::game {
///////////////////////////////////////////////////////////////////////////////
// SceneObjectInfo
///////////////////////////////////////////////////////////////////////////////
#pragma region SceneObjectInfo

#pragma endregion

///////////////////////////////////////////////////////////////////////////////
// Scene
///////////////////////////////////////////////////////////////////////////////
#pragma region Scene

Scene* Scene::current_scene_ = nullptr;

Scene::Scene(GameManager* gm, const SceneInfo& info)
    : info_(info)
    , gm_(gm) {
  object_factory_ = std::make_unique<ObjectFactory>(this, gm);
  // 默认就会创建用于角色的Collider管理器
  CreateCharaColliderWorld();
}

void Scene::Load() {
  object_factory_->SetupEnvironment();

  auto dx = gm_->GetGraphics();
  auto assets = gm_->GetAssets();
  auto res = gm_->GetResources();

  // 预备载入
  res->ReadyCommand();

  // 创建描述符堆
  dx->BuildDescriptorHeaps();

  // 载入系统的项目
  gm_->Info()["SystemTexture"].Foreach(
      "Item", [res, assets](ikit::XmlNode& node, int index) {
        res->LoadTexture(assets->GetTexture(node.Value()));
      });
  gm_->Info()["SystemMaterial"].Foreach(
      "Item", [res, assets](ikit::XmlNode& node, int index) {
        res->LoadMaterial(assets->GetMaterial(node.Value()));
      });
  gm_->Info()["SystemModel"].Foreach(
      "Item", [res, assets](ikit::XmlNode& node, int index) {
        res->LoadModel(assets->GetModel(node.Value()));
      });

  // 载入UI所属的Texture
  for (auto& p : gm_->GetUIManager()->Sprites()) {
    res->LoadTexture(p.get());
  }


  // 设置默认项目
  assets->SetupDefaultAssets();
  // 设置空白法线等
  res->SetupSystemResource();

  //  创建DEBUG层
  BuildDebugLayer();

  // 创建UI
  // BuildUI();

  res->CommitCommand();

  gm_->GetLua()->OnLoaded();

  // 获得FPS的项目
  auto ui = gm_->GetUIManager();
  fps_text_ = ui->FindLabel("Fps");
  debug_text_ = ui->FindLabel("Status");

  // 获得水体
  water_ = assets->GetMaterial("SYS_WATER");

  KLOG("Load Scene: %s", info_.name.c_str());
}

void Scene::SetRenderObject(ikit::sstr name, dx::RenderItem* item) {
  auto assets = gm_->GetAssets();
  auto& mesh = assets->GetMesh(name);
  mesh.SetRenderObject(item);
}

PhysicsWorld* Scene::CreatePhysicsWorld(ikit::sstr name) {
  auto item = std::make_unique<PhysicsWorld>(*this, name);
  auto ptr = item.get();
  physics_worlds_.push_back(std::move(item));
  return ptr;
}

PhysicsWorld* Scene::GetPhysicsWorld(ikit::sstr name) {
  for (auto& e : physics_worlds_) {
    if (e->name == name) {
      return e.get();
    }
  }
  return nullptr;
}

ColliderWorld* Scene::CreateCharaColliderWorld() {
  chara_collider_ = std::make_unique<ColliderWorld>(*this, "Chara");
  return chara_collider_.get();
}

ColliderWorld* Scene::GetCharaColliderWorld() {
  return chara_collider_.get();
}

ui::UIManager* Scene::GetUIManager() { return gm_->GetUIManager(); }

void Scene::BuildDebugLayer() {
  auto dx = gm_->GetGraphics();
  auto res = gm_->GetResources();
  auto item = res->CreateRenderItem(dx::RenderLayer::Debug);
  // 任意设置一个材质即可，只是防止出问题
  item->material = res->GetMaterial(0);
  SetRenderObject("QUAD", item);
}

static char fps_buffer[256];

void Scene::FixedUpdate() {
  float delta_time = g_timer.fixedDeltaTime;

  auto dx = gm_->GetGraphics();
  if (fps_text_ != nullptr) {
    sprintf(fps_buffer, "FPS:%.0f", g_timer.GetFps());
    fps_text_->SetText(fps_buffer);
  }

  // 进行Update更新
  object_factory_->FixedUpdate();
  // PS：这里物理系统要后于Object更新
  for (auto& e : physics_worlds_) {
    e->FixedUpdate(delta_time);
  }

  // 执行Lua
  gm_->GetLua()->OnFixedUpdate();

  // 执行LateUpdate更新
  // PS：这里物理系统要先于Object更新。
  for (auto& e : physics_worlds_) {
    e->LateUpdate(delta_time);
  }
  object_factory_->LateUpdate();

  // 如果使用了水体，则进行更新
  if (water_ && water_->GetMaterial()) {
    water_->GetMaterial()->AnimateWaterMaterials();
  }
}

size_t Scene::PrintDebug(const char* format, ...) {
  if (debug_text_ == nullptr) {
    return 0;
  }

  va_list args;
  va_start(args, format);
  size_t result = debug_text_->VPrint(format, args);
  va_end(args);
  return result;
}

void Scene::SetDebugMessage(int index, const char* text, size_t len) {
  debug_text_->SetText(text, len);
}
#pragma endregion
} // namespace ifire::game
